home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / IFC_112 / netscape / util / Hashtable.java < prev    next >
Encoding:
Text File  |  1999-05-28  |  16.2 KB  |  587 lines  |  [TEXT/CWIE]

  1. // Hashtable.java
  2. // By Ned Etcode
  3. // Copyright 1995, 1996, 1997 Netscape Communications Corp.  All rights reserved.
  4.  
  5. package netscape.util;
  6.  
  7. // Each entry in the table can be in one of three states:
  8. //   1. Empty   -> hashCodes[index] == EMPTY
  9. //   2. Removed -> hashCodes[index] == REMOVED
  10. //   3. Filled  -> keys[index] != null
  11.  
  12. // You must call Hashtable.hash() to get the real hash code for this table.
  13.  
  14. // There needs to be a way to call Object.hashCode() directly when you want
  15. // to do identity hashing.  ALERT!
  16.  
  17. /** Object subclass that implements a hash table.
  18.   * @note 1.0 toString() prints as formatted text
  19.   */
  20. public class Hashtable implements Cloneable, Codable {
  21.     /** For the multiplicative hash, choose the golden ratio:
  22.       * <pre>
  23.       *     A = ((sqrt(5) - 1) / 2) * (1 << 32)
  24.       * </pre>
  25.       * ala Knuth...
  26.       */
  27.     static final int A = 0x9e3779b9;
  28.  
  29.     /** We use EMPTY and REMOVED as special markers in the table. If some
  30.       * poor object returns one of these two values as their hashCode, it
  31.       * is wacked to DEFAULT.
  32.       */
  33.     static final int EMPTY = 0;
  34.     static final int REMOVED = 1;
  35.     static final int DEFAULT = 2;
  36.  
  37.     static final String keysField = "keys";
  38.     static final String elementsField = "elements";
  39.  
  40.     int count;
  41.     int totalCount;
  42.     int shift;
  43.     int capacity;
  44.     int indexMask;
  45.  
  46.     int hashCodes[];
  47.     Object keys[];
  48.     Object elements[];
  49.  
  50.     /** Constructs an empty Hashtable. The Hashtable will grow on demand
  51.       * as more elements are added.
  52.       */
  53.     public Hashtable() {
  54.         super();
  55.         shift = 32 - 3 + 1;
  56.     }
  57.  
  58.     /** Constructs a Hashtable capable of holding at least
  59.       * <b>initialCapacity</b> elements before needing to grow.
  60.       */
  61.     public Hashtable(int initialCapacity) {
  62.         this();
  63.  
  64.         if (initialCapacity < 0)
  65.             throw new IllegalArgumentException("initialCapacity must be > 0");
  66.  
  67.         grow(initialCapacity);
  68.     }
  69.  
  70.     /** Creates a shallow copy of the Hashtable. The table itself is cloned,
  71.       * but none of the keys or elements are copied.
  72.       */
  73.     public Object clone() {
  74.         int len;
  75.         Hashtable newTable;
  76.  
  77.         try {
  78.             newTable = (Hashtable)super.clone();
  79.         } catch (CloneNotSupportedException e) {
  80.             throw new InternalError(
  81.                 "Error in clone(). This shouldn't happen.");
  82.         }
  83.  
  84.         // If there is nothing in the table, then we cloned to make sure the
  85.         // class was preserved, but just null everything out except for shift,
  86.         // which implies the default initial capacity.
  87.  
  88.         if (count == 0) {
  89.             newTable.shift = 32 - 3 + 1;
  90.             newTable.totalCount = 0;
  91.             newTable.capacity = 0;
  92.             newTable.indexMask = 0;
  93.             newTable.hashCodes = null;
  94.             newTable.keys = null;
  95.             newTable.elements = null;
  96.  
  97.             return newTable;
  98.         }
  99.  
  100.         len = hashCodes.length;
  101.         newTable.hashCodes = new int[len];
  102.         newTable.keys = new Object[len];
  103.         newTable.elements = new Object[len];
  104.  
  105.         System.arraycopy(hashCodes, 0, newTable.hashCodes, 0, len);
  106.         System.arraycopy(keys, 0, newTable.keys, 0, len);
  107.         System.arraycopy(elements, 0, newTable.elements, 0, len);
  108.  
  109.         return newTable;
  110.     }
  111.  
  112.     /** Returns the number of elements in the Hashtable.
  113.       */
  114.     public int count() {
  115.         return count;
  116.     }
  117.  
  118.     /** Returns the number of elements in the Hashtable.
  119.       */
  120.     public int size() {
  121.         return count;
  122.     }
  123.  
  124.     /** Returns <b>true</b> if there are no elements in the Hashtable.
  125.       */
  126.     public boolean isEmpty() {
  127.         return (count == 0);
  128.     }
  129.  
  130.     /** Returns an Enumeration of the Hashtable's keys.
  131.       * @see #elements
  132.       */
  133.     public Enumeration keys() {
  134.         return new HashtableEnumerator(this, true);
  135.     }
  136.  
  137.     /** Returns an Enumeration of the Hashtable's elements.
  138.       * @see #keys
  139.       */
  140.     public Enumeration elements() {
  141.         return new HashtableEnumerator(this, false);
  142.     }
  143.  
  144.     /** Returns a Vector containing the Hashtable's keys.
  145.       */
  146.     public Vector keysVector() {
  147.         int i, vectCount;
  148.         Object key;
  149.         Vector vect;
  150.  
  151.         if (count == 0)
  152.             return new Vector();
  153.  
  154.         vect = new Vector(count);
  155.         vectCount = 0;
  156.  
  157.         for (i = 0; i < keys.length && vectCount < count; i++) {
  158.             key = keys[i];
  159.             if (key != null) {
  160.                 vect.addElement(key);
  161.                 vectCount++;
  162.             }
  163.         }
  164.  
  165.         return vect;
  166.     }
  167.  
  168.     /** Returns a Vector containing the Hashtable's elements.
  169.       */
  170.     public Vector elementsVector() {
  171.         int i, vectCount;
  172.         Object element;
  173.         Vector vect;
  174.  
  175.         if (count == 0)
  176.             return new Vector();
  177.  
  178.         vect = new Vector(count);
  179.         vectCount = 0;
  180.  
  181.         for (i = 0; i < elements.length && vectCount < count; i++) {
  182.             element = elements[i];
  183.             if (element != null) {
  184.                 vect.addElement(element);
  185.                 vectCount++;
  186.             }
  187.         }
  188.  
  189.         return vect;
  190.     }
  191.  
  192.     /** Returns an Object array containing the Hashtable's keys.
  193.       */
  194.     public Object[] keysArray() {
  195.         int i, arrayCount;
  196.         Object key, array[];
  197.  
  198.         if (count == 0)
  199.             return null;
  200.  
  201.         array = new Object[count];
  202.         arrayCount = 0;
  203.  
  204.         for (i = 0; i < keys.length && arrayCount < count; i++) {
  205.             key = keys[i];
  206.             if (key != null) {
  207.                 array[arrayCount++] = key;
  208.             }
  209.         }
  210.  
  211.         return array;
  212.     }
  213.  
  214.     /** Returns an Object array containing the Hashtable's elements.
  215.       */
  216.     public Object[] elementsArray() {
  217.         int i, arrayCount;
  218.         Object element, array[];
  219.  
  220.         if (count == 0)
  221.             return null;
  222.  
  223.         array = new Object[count];
  224.         arrayCount = 0;
  225.  
  226.         for (i = 0; i < elements.length && arrayCount < count; i++) {
  227.             element = elements[i];
  228.             if (element != null) {
  229.                 array[arrayCount++] = element;
  230.             }
  231.         }
  232.  
  233.         return array;
  234.     }
  235.  
  236.     /** Returns <b>true</b> if the Hashtable contains the element. This method
  237.       * is slow -- O(n) -- because it must scan the table searching for the
  238.       * element.
  239.       */
  240.     public boolean contains(Object element) {
  241.         int i;
  242.         Object tmp;
  243.  
  244.         // We need to short-circuit here since the data arrays may not have
  245.         // been allocated yet.
  246.  
  247.         if (count == 0)
  248.             return false;
  249.  
  250.         if (element == null)
  251.             throw new NullPointerException();
  252.  
  253.         if (elements == null)
  254.             return false;
  255.  
  256.         for (i = 0; i < elements.length; i++) {
  257.             tmp = elements[i];
  258.             if (tmp != null && element.equals(tmp))
  259.                 return true;
  260.         }
  261.  
  262.         return false;
  263.     }
  264.  
  265.     /** Returns <b>true</b> if the Hashtable contains the key <b>key</b>.
  266.       */
  267.     public boolean containsKey(Object key) {
  268.         return (get(key) != null);
  269.     }
  270.  
  271.     /** Returns the element associated with the <b>key</b>. This method returns
  272.       * <b>null</b> if the Hashtable does not contain <b>key</b>.  Hashtable
  273.       * hashes and compares <b>key</b> using <b>hashCode()</b> and
  274.       * <b>equals()</b>.
  275.       */
  276.     public Object get(Object key) {
  277.         // We need to short-circuit here since the data arrays may not have
  278.         // been allocated yet.
  279.  
  280.         if (count == 0)
  281.             return null;
  282.  
  283.         return elements[tableIndexFor(key, hash(key))];
  284.     }
  285.  
  286.     /** Removes <b>key</b> and the element associated with it from the
  287.       * Hashtable. Returns the element associated with <b>key</b>, or
  288.       * <b>null</b> if <b>key</b> was not present.
  289.       */
  290.     public Object remove(Object key) {
  291.         int index;
  292.         Object oldValue;
  293.  
  294.         // We need to short-circuit here since the data arrays may not have
  295.         // been allocated yet.
  296.  
  297.         if (count == 0)
  298.             return null;
  299.  
  300.         index = tableIndexFor(key, hash(key));
  301.         oldValue = elements[index];
  302.         if (oldValue == null)
  303.             return null;
  304.  
  305.         count--;
  306.         hashCodes[index] = REMOVED;
  307.         keys[index] = null;
  308.         elements[index] = null;
  309.  
  310.         return oldValue;
  311.     }
  312.  
  313.     /** Places the <b>key</b>/<b>element</b> pair in the Hashtable. Neither
  314.       * <b>key</b> nor <b>element</b> may be <b>null</b>. Returns the old
  315.       * element associated with <b>key</b>, or <b>null</b> if the <b>key</b>
  316.       * was not present.
  317.       */
  318.     public Object put(Object key, Object element) {
  319.         int index, hash;
  320.         Object oldValue;
  321.  
  322.         if (element == null)
  323.             throw new NullPointerException();
  324.  
  325.         // Since we delay allocating the data arrays until we actually need
  326.         // them, check to make sure they exist before attempting to put
  327.         // something in them.
  328.  
  329.         if (hashCodes == null)
  330.             grow();
  331.  
  332.         hash = hash(key);
  333.         index = tableIndexFor(key, hash);
  334.         oldValue = elements[index];
  335.  
  336.         // If the total number of occupied slots (either with a real element or
  337.         // a removed marker) gets too big, grow the table.
  338.  
  339.         if (oldValue == null) {
  340.             if (hashCodes[index] == EMPTY) {
  341.                 if (totalCount >= capacity) {
  342.                     grow();
  343.                     return put(key, element);
  344.                 }
  345.                 totalCount++;
  346.             }
  347.             count++;
  348.         }
  349.  
  350.         hashCodes[index] = hash;
  351.         keys[index] = key;
  352.         elements[index] = element;
  353.  
  354.         return oldValue;
  355.     }
  356.  
  357.     /** We preclude the hashCodes EMPTY and REMOVED because we use them to
  358.       * indicate empty and previously filled slots in the table. All the
  359.       * Hashtable code should go through here and not call hashCode()
  360.       * directly on the key.
  361.       */
  362.     private int hash(Object key) {
  363.         int hash;
  364.  
  365.         // On sparc it appears that the last 3 bits of Object.hashCode() are
  366.         // insignificant!  ALERT!
  367.  
  368.         hash = key.hashCode();
  369.         if (hash == EMPTY || hash == REMOVED)
  370.             hash = DEFAULT;
  371.  
  372.         return hash;
  373.     }
  374.  
  375.     /** Primitive method used internally to find slots in the
  376.       * table. If the key is present in the table, this method will return the
  377.       * index
  378.       * under which it is stored. If the key is not present, then this
  379.       * method will return the index under which it can be put. The caller
  380.       * must look at the hashCode at that index to differentiate between
  381.       * the two possibilities.
  382.       */
  383.     private int tableIndexFor(Object key, int hash) {
  384.         int product, testHash, index, step, removedIndex, probeCount;
  385.  
  386.         product = hash * A;
  387.         index = product >>> shift;
  388.  
  389.         // Probe the first slot in the table.  We keep track of the first
  390.         // index where we found a REMOVED marker so we can return that index
  391.         // as the first available slot if the key is not already in the table.
  392.  
  393.         testHash = hashCodes[index];
  394.  
  395.         if (testHash == hash) {
  396.             if (key.equals(keys[index]))
  397.                 return index;
  398.             removedIndex = -1;
  399.         } else if (testHash == EMPTY)
  400.             return index;
  401.         else if (testHash == REMOVED)
  402.             removedIndex = index;
  403.         else
  404.             removedIndex = -1;
  405.  
  406.         // Our first probe has failed, so now we need to start looking
  407.         // elsewhere in the table.
  408.  
  409.         step = ((product >>> (2 * shift - 32)) & indexMask) | 1;
  410.         probeCount = 1;
  411.  
  412.         do {
  413.             probeCount++;
  414.             index = (index + step) & indexMask;
  415.  
  416.             testHash = hashCodes[index];
  417.  
  418.             if (testHash == hash) {
  419.                 if (key.equals(keys[index]))
  420.                     return index;
  421.             } else if (testHash == EMPTY) {
  422.                 if (removedIndex < 0)
  423.                     return index;
  424.                 else
  425.                     return removedIndex;
  426.             } else if (testHash == REMOVED && removedIndex == -1)
  427.                 removedIndex = index;
  428.  
  429.         } while (probeCount <= totalCount);
  430.  
  431.         // Something very bad has happened.
  432.  
  433.         throw new InconsistencyException("Hashtable overflow");
  434.     }
  435.  
  436.     /** Grows the table to accommodate at least capacity number of elements.
  437.       */
  438.     private void grow(int capacity) {
  439.         int tableSize, power;
  440.  
  441.         // Find the lowest power of 2 size for the table which will allow
  442.         // us to insert initialCapacity number of objects before having to
  443.         // grow.
  444.  
  445.         tableSize = (capacity * 4) / 3;
  446.  
  447.         power = 3;
  448.         while ((1 << power) < tableSize)
  449.             power++;
  450.  
  451.         // Once shift is set, then grow() will do the right thing when
  452.         // called.
  453.  
  454.         shift = 32 - power + 1;
  455.         grow();
  456.     }
  457.  
  458.     /** Grows the table by a factor of 2 (or creates it if necessary). All
  459.       * the REMOVED markers go away and the elements are rehashed into the
  460.       * bigger table.
  461.       */
  462.     private void grow() {
  463.         int i, index, hash, power, oldHashCodes[];
  464.         Object key, oldKeys[], oldValues[];
  465.  
  466.         // The table size needs to be a power of two, and it should double
  467.         // when it grows.  We grow when we are more than 3/4 full.
  468.  
  469.         shift--;
  470.         power = 32 - shift;
  471.         indexMask = (1 << power) - 1;
  472.         capacity = (3 * (1 << power)) / 4;
  473.  
  474.         oldHashCodes = hashCodes;
  475.         oldKeys = keys;
  476.         oldValues = elements;
  477.  
  478.         hashCodes = new int[1 << power];
  479.         keys = new Object[1 << power];
  480.         elements = new Object[1 << power];
  481.  
  482.         // Reinsert the old elements into the new table if there are any.  Be
  483.         // sure to reset the counts and increment them as the old entries are
  484.         // put back in the table.
  485.  
  486.         totalCount = 0;
  487.  
  488.         if (count > 0) {
  489.             count = 0;
  490.  
  491.             for (i = 0; i < oldHashCodes.length; i++) {
  492.                 key = oldKeys[i];
  493.  
  494.                 if (key != null) {
  495.                     hash = oldHashCodes[i];
  496.                     index = tableIndexFor(key, hash);
  497.  
  498.                     hashCodes[index] = hash;
  499.                     keys[index] = key;
  500.                     elements[index] = oldValues[i];
  501.  
  502.                     count++;
  503.                     totalCount++;
  504.                 }
  505.             }
  506.         }
  507.     }
  508.  
  509.     /** Removes all keys and elements from the Hashtable.
  510.       */
  511.     public void clear() {
  512.         int i;
  513.  
  514.         if (hashCodes == null)
  515.             return;
  516.  
  517.         for (i = 0; i < hashCodes.length; i++) {
  518.             hashCodes[i] = EMPTY;
  519.             keys[i] = null;
  520.             elements[i] = null;
  521.         }
  522.  
  523.         count = 0;
  524.         totalCount = 0;
  525.     }
  526.  
  527.     /** Returns a string serialization of the Hashtable using the
  528.       * Serializer.
  529.       * @see Serializer
  530.       */
  531.     public String toString() {
  532.         return FormattingSerializer.serializeObject(this);
  533.     }
  534.  
  535.     /** Describes the Hashtable class' information.
  536.       * @see Codable#describeClassInfo
  537.       */
  538.     public void describeClassInfo(ClassInfo info) {
  539.         info.addClass("netscape.util.Hashtable", 1);
  540.         info.addField(keysField, OBJECT_ARRAY_TYPE);
  541.         info.addField(elementsField, OBJECT_ARRAY_TYPE);
  542.     }
  543.  
  544.     /** Encodes the Hashtable instance. All the keys and elements must be
  545.       * Codable.
  546.       * @see Codable#encode
  547.       */
  548.     public void encode(Encoder encoder) throws CodingException {
  549.         Object keysArray[], elementsArray[];
  550.  
  551.         if (count == 0)
  552.             return;
  553.  
  554.         keysArray = keysArray();
  555.         elementsArray = elementsArray();
  556.  
  557.         encoder.encodeObjectArray(keysField, keysArray, 0, keysArray.length);
  558.         encoder.encodeObjectArray(elementsField, elementsArray, 0,
  559.             elementsArray.length);
  560.     }
  561.  
  562.     /** Decodes the Hashtable instance.
  563.       * @see Codable#decode
  564.       */
  565.     public void decode(Decoder decoder) throws CodingException {
  566.         int i;
  567.         Object keysArray[], elementsArray[];
  568.  
  569.         keysArray = decoder.decodeObjectArray(keysField);
  570.         elementsArray = decoder.decodeObjectArray(elementsField);
  571.  
  572.         if (keysArray == null || keysArray.length == 0)
  573.             return;
  574.  
  575.         grow(keysArray.length);
  576.  
  577.         for (i = 0; i < keysArray.length; i++)
  578.             put(keysArray[i], elementsArray[i]);
  579.     }
  580.  
  581.     /** Finishes the Hashtable's decoding.
  582.       * @see Codable#finishDecoding
  583.       */
  584.     public void finishDecoding() throws CodingException {
  585.     }
  586. }
  587.